using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public class Stall
{
    public void stall()
    {
        //stall2();
    }

    public int stall2()
    {
        int aantal = 0;

        for (int i = 2; i < 0xffff; i++)
        {
            bool priem = true;

            for (int j = 2; j < i; j++)
                if (i % j == 0)
                    priem = false;

            if (priem)
                aantal++;
        }

        return aantal;
    }
}

public class MyDirectoryInfo
{
    public DirectoryInfo dir;

    public MyDirectoryInfo() : this(new DirectoryInfo(Directory.GetCurrentDirectory())) { }
    public MyDirectoryInfo(DirectoryInfo dir) { this.dir = dir; }

    public DirectoryInfo up()
    {
        if (dir.Parent != null)
            return chdir(dir.Parent);

        return dir;
    }

    public DirectoryInfo chdir(DirectoryInfo dir)
    {
        Directory.SetCurrentDirectory(dir.FullName);
        return this.dir = dir;
    }
}

public class MyList : List<FileSystemInfo>
{
    public MyList() { }

    public MyList(List<FileSystemInfo> list)
    {
        foreach (FileSystemInfo fsi in list)
        {
            if (fsi.GetType().Equals(typeof(FileInfo)))
            {
                Add(fsi);
            }
            else if (fsi.GetType().Equals(typeof(DirectoryInfo)))
            {
                foreach (FileInfo fi in ((DirectoryInfo)fsi).EnumerateFiles())
                    Add(fi);
            }
        }
    }

    public List<string> fileNames()
    {
        List<string> returnList = new List<string>();

        foreach (FileSystemInfo fsi in this)
            returnList.Add(fsi.FullName);

        return returnList;
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();

        foreach (FileSystemInfo fsi in this)
        {
            sb.Append(fsi);
            sb.Append("\n");
        }

        return sb.ToString();
    }

    public void delete()
    {
        foreach (FileSystemInfo fsi in this)
            fsi.Delete();
    }
}

public class Pool
{
    private readonly LinkedList<Thread> availableWorkers;
    public readonly LinkedList<Action> _tasks = new LinkedList<Action>();
    public readonly LinkedList<Thread> busyWorkers;
    private bool _disallowAdd;
    private bool _disposed;
    public int completedCount = 0;
    public int workerIndex = 0;
    public int intendedSize;

    public int actualSize()
    {
        return availableWorkers.Count + busyWorkers.Count;
    }

    public Pool() : this(5) { }

    public Pool(int size)
    {
        intendedSize = size;
        availableWorkers = new LinkedList<Thread>();
        busyWorkers = new LinkedList<Thread>();

        for (int i = 0; i < size; i++)
            newWorker();
    }

    public void setSize(int size)
    {
        intendedSize = size;
        int actualSize2 = actualSize();

        for (int i = 0; i < (intendedSize - actualSize2); i++)
            newWorker();
            
    }

    public void newWorker()
    {
        Thread worker = new Thread(Worker);
        worker.Name = string.Concat("Worker ", workerIndex++);
        worker.Start();
        availableWorkers.AddLast(worker);
    }

    public void quit()
    {
        var waitForThreads = false;

        lock (this._tasks)
        {
            if (!this._disposed)
            {
                GC.SuppressFinalize(this);

                this._disallowAdd = true;

                while (_tasks.Count > 0)
                    Monitor.Wait(this._tasks);

                this._disposed = true;
                Monitor.PulseAll(this._tasks);
                waitForThreads = true;
            }
        }

        if (waitForThreads)
        {
            while (busyWorkers.Count > 0)
                Thread.Sleep(10);

            foreach (var worker in availableWorkers)
                worker.Join();
        }
    }

    public void clearQueue()
    {
        lock (_tasks)
        {
            _tasks.Clear();
            Monitor.PulseAll(_tasks);
        }
    }

    public void QueueTask(Action task)
    {
        lock (this._tasks)
        {
            //if (this._disallowAdd) { throw new InvalidOperationException("This Pool instance is in the process of being disposed, can't add anymore"); }
            //if (this._disposed) { throw new ObjectDisposedException("This Pool instance has already been disposed"); }
            this._tasks.AddLast(task);
            Monitor.PulseAll(this._tasks); // pulse because tasks count changed
        }
    }

    private void Worker()
    {
        Action task = null;

        while (true)
        {
            lock (this._tasks)
            {
                while (true)
                {
                    if (this._disposed)
                        return;

                    if (null != availableWorkers.First && object.ReferenceEquals(Thread.CurrentThread, availableWorkers.First.Value) && this._tasks.Count > 0)
                    {
                        task = this._tasks.First.Value;
                        this._tasks.RemoveFirst();
                        availableWorkers.RemoveFirst();
                        busyWorkers.AddLast(Thread.CurrentThread);
                        Monitor.PulseAll(this._tasks);
                        break;
                    }

                    Monitor.Wait(this._tasks);
                }
            }

            task();

            lock (this._tasks)
            {
                completedCount++;
                busyWorkers.Remove(Thread.CurrentThread);

                if (actualSize() < intendedSize)
                    availableWorkers.AddLast(Thread.CurrentThread);
            }
            task = null;
        }
    }
}

public class MyFileStream : FileStream
{
    public MyFileStream(string path, FileMode mode, FileAccess access) : base(path, mode, access) { }

    public MyMD5Sum md5()
    {
        MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
        byte[] buffer = new byte[Length];
        Read(buffer, 0, (int)Length);
        hasher.ComputeHash(buffer);
        return new MyMD5Sum(hasher.Hash);
    }
}

public class MyMemoryStream : MemoryStream
{
    public MyMemoryStream() { }
    public MyMemoryStream(int capacity) : base(capacity) { }

    public MyMD5Sum md5()
    {
        MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
        hasher.ComputeHash(this.ToArray());
        return new MyMD5Sum(hasher.Hash);
    }
}

public class MyMD5Sum
{
    public byte[] md5sum;

    public MyMD5Sum(byte[] md5sum) { this.md5sum = md5sum; }
    public bool compare(MyMD5Sum md5sum) { return this.md5sum.SequenceEqual(md5sum.md5sum); }
}

public class MyByteArray
{
    public byte[] deArray;

    public MyByteArray(byte[] deArray) { this.deArray = deArray; }
    public long getSize() { return deArray.Length; }

    public MyMD5Sum md5()
    {
        MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
        hasher.ComputeHash(deArray);
        return new MyMD5Sum(hasher.Hash);
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();

        foreach (byte bijt in deArray)
            sb.Append(bijt.ToString("X2"));

        return sb.ToString();
    }
}

